/** \file    ForeignSegments.cpp
  * \brief   Produces "foreign" segment for each border segment of mask.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/segm/src/ForeignSegments.cpp,v 1.2 2005/04/04 04:10:16 cscom1r4 Exp $
  */

#include "segm/Segmentation.h"
#include "segm/VecInd.h"
#include "xtd/kDimTree.h"
#include <algorithm>
#include "vec2d/container.h"
#include "std_min_max.h"
#include "imglib/bitmap.h"
#include "imglib/bmpmorph.h"

ForeignSegments::~ForeignSegments()
{
	if (_made)
		_s.removeForeignSegments();
}

void ForeignSegments::make(const img::bitmap & foreignArea)
{
	assert(!_made);
	_s.makeForeignSegments(foreignArea);
	_made = true;
}

void ForeignSegments::make(const img::bitmap & mask, unsigned foreignAreaWidth)
{
	img::bitmap foreignArea;
	img::dilate4(foreignArea, mask, foreignAreaWidth);
	img::combine(foreignArea, mask, img::bit_and_not());
	make(foreignArea);
}

static inline void prepSegm( Segment &sg ) {
	sg.foreign = false;
	sg.active = false;
}

/** Does not modifies m_segmentMap since foreign segments receive the same color as border segments.
  * Also activates border segments and deactivates other */
void Segmentation::makeForeignSegments( 
	const img::bitmap &frgnArea )	///< gives area around mask from where to form foreign segments
{
	std::for_each( segmentsBegin(), segmentsEnd(), prepSegm );
	int w = width();
	int h = height();
	vec2d::Vec2i p;

	//find all border pixels
	vec2d::Vec2iArr borderPixelsArr;
	for( p.y = 0; p.y != h; ++p.y ) {
		for( p.x = 0; p.x != w; ++p.x ) {
			Segment *g = pSegment(p.x,p.y);
			if( g ) {
				if( !((p.x==0) ? 0 : pSegment(p.x-1,p.y)) ||
					!((p.x+1==w) ? 0 : pSegment(p.x+1,p.y)) ||
					!((p.y==0) ? 0 : pSegment(p.x,p.y-1)) ||
					!((p.y+1==h) ? 0 : pSegment(p.x,p.y+1)) )
				{
					borderPixelsArr.push_back(p);
					g->active = true;
				}
			}
		}
	}
	Vec2iTree borderPixelsTree;
	borderPixelsTree.Create( &*borderPixelsArr.begin(), borderPixelsArr.size() );

	//start of real making
	IntIntMap map; // key-border segment index, value-new foreign segment index
	for( p.y = 0; p.y != h; ++p.y ) {
		for( p.x = 0; p.x != w; ++p.x ) {
			if( !frgnArea.at(p.x,p.y) ) continue;
			assert( m_segmentCoordMap(p.x,p.y) == -1 );
			const vec2d::Vec2i *n = borderPixelsTree.FindNearestPoint( p, DBL_MAX );
			assert( n );
			int bi = m_segmentCoordMap(n->x,n->y);
			assert( bi != -1 );
			//!m_segments is changing in size here, do not save pointers on its members
			small_byte_color gColor = segmentByIndex(bi).color;
			//add p point to foreign segment corresponding to border segment g
			IntIntMap::iterator mi = map.find(bi);
			if( mi == map.end() ) {
				//first pixel in foreign segment
				size_t n = m_segmentIvector.append(Segment(gColor));
				mi = map.insert( IntIntMap::value_type(bi, n) ).first;
				m_segmentIvector[n].foreign = true;
				m_segmentIvector[n].active = false;
			} else {
				assert(segmentByIndex(mi->second).color == gColor);
			}
			++segmentByIndex(m_segmentCoordMap(p.x,p.y) = mi->second).pixelCount;
		}
	}
	changed();
}

void Segmentation::removeForeignSegments()
{
	//assume that all foreign segments are in the tail of m_segments
	int firstForeignSegment = count();
	int w = width();
	int h = height();
	vec2d::Vec2i p;
	for( p.y = 0; p.y != h; ++p.y ) {
		for( p.x = 0; p.x != w; ++p.x ) {
			int fi = m_segmentCoordMap(p.x,p.y);
			if( fi == -1 ) continue;
			Segment &g = segmentByIndex(fi);
			if( g.foreign ) {
				firstForeignSegment = std::min( fi, firstForeignSegment );
				m_segmentCoordMap(p.x,p.y) = -1;
			}
		}
	}
	m_segmentIvector.reduce( firstForeignSegment );
	changed();
}
